home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / ThreadGroup.java < prev    next >
Text File  |  1998-09-22  |  27KB  |  834 lines

  1. /*
  2.  * @(#)ThreadGroup.java    1.34 98/07/01
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  * 
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.lang;
  16.  
  17. import java.io.PrintStream;
  18. import sun.misc.VM;
  19.  
  20. /**
  21.  * A thread group represents a set of threads. In addition, a thread 
  22.  * group can also include other thread groups. The thread groups form 
  23.  * a tree in which every thread group except the initial thread group 
  24.  * has a parent. 
  25.  * <p>
  26.  * A thread is allowed to access information about its own thread 
  27.  * group, but not to access information about its thread group's 
  28.  * parent thread group or any other thread groups. 
  29.  *
  30.  * @author  unascribed
  31.  * @version 1.34, 07/01/98
  32.  * @since   JDK1.0
  33.  */
  34. /* The locking strategy for this code is to try to lock only one level of the
  35.  * tree wherever possible, but otherwise to lock from the bottom up.
  36.  * That is, from child thread groups to parents.
  37.  * This has the advantage of limiting the number of locks that need to be held
  38.  * and in particular avoids having to grab the lock for the root thread group,
  39.  * (or a global lock) which would be a source of contention on a 
  40.  * multi-processor system with many thread groups.
  41.  * This policy often leads to taking a snapshot of the state of a thread group
  42.  * and working off of that snapshot, rather than holding the thread group locked
  43.  * while we work on the children.
  44.  */
  45. public
  46. class ThreadGroup {
  47.     ThreadGroup parent;
  48.     String name;
  49.     int maxPriority;
  50.     boolean destroyed;
  51.     boolean daemon;
  52.     boolean vmAllowSuspension;
  53.  
  54.     int nthreads;
  55.     Thread threads[];
  56.  
  57.     int ngroups;
  58.     ThreadGroup groups[];
  59.  
  60.     /**
  61.      * Creates an empty Thread group that is not in any Thread group. 
  62.      * This method is used to create the system Thread group.
  63.      */
  64.     private ThreadGroup() {    // called from C code
  65.     this.name = "system";
  66.     this.maxPriority = Thread.MAX_PRIORITY;
  67.     }
  68.  
  69.     /**
  70.      * Constructs a new thread group. The parent of this new group is 
  71.      * the thread group of the currently running thread. 
  72.      *
  73.      * @param   name   the name of the new thread group.
  74.      * @since   JDK1.0
  75.      */
  76.     public ThreadGroup(String name) {
  77.     this(Thread.currentThread().getThreadGroup(), name);
  78.     }
  79.  
  80.     /**
  81.      * Creates a new thread group. The parent of this new group is the 
  82.      * specified thread group. 
  83.      * <p>
  84.      * The <code>checkAccess</code> method of the parent thread group is 
  85.      * called with no arguments; this may result in a security exception. 
  86.      *
  87.      * @param     parent   the parent thread group.
  88.      * @param     name     the name of the new thread group.
  89.      * @exception  NullPointerException  if the thread group argument is
  90.      *               <code>null</code>.
  91.      * @exception  SecurityException  if the current thread cannot create a
  92.      *               thread in the specified thread group.
  93.      * @see     java.lang.SecurityException
  94.      * @see     java.lang.ThreadGroup#checkAccess()
  95.      * @since   JDK1.0
  96.      */
  97.     public ThreadGroup(ThreadGroup parent, String name) {
  98.     if (parent == null) {
  99.         throw new NullPointerException();
  100.     }
  101.     parent.checkAccess();
  102.     this.name = name;
  103.     this.maxPriority = parent.maxPriority;
  104.     this.daemon = parent.daemon;
  105.     this.vmAllowSuspension = parent.vmAllowSuspension;
  106.     this.parent = parent;
  107.     parent.add(this);
  108.     }
  109.  
  110.     /**
  111.      * Returns the name of this thread group.
  112.      *
  113.      * @return  the name of this thread group.
  114.      * @since   JDK1.0
  115.      */
  116.     public final String getName() {
  117.     return name;
  118.     }
  119.  
  120.     /**
  121.      * Returns the parent of this thread group.
  122.      *
  123.      * @return  the parent of this thread group. The top-level thread group
  124.      *          is the only thread group whose parent is <code>null</code>.
  125.      * @since   JDK1.0
  126.      */
  127.     public final ThreadGroup getParent() {
  128.         checkAccess();
  129.     return parent;
  130.     }
  131.  
  132.     /**
  133.      * Returns the maximum priority of this thread group. Threads that are
  134.      * part of this group cannot have a higher priority than the maximum
  135.      * priority.
  136.      *
  137.      * @return  the maximum priority that a thread in this thread group
  138.      *          can have.
  139.      * @since   JDK1.0
  140.      */
  141.     public final int getMaxPriority() {
  142.     return maxPriority;
  143.     }
  144.  
  145.     /**
  146.      * Tests if this thread group is a daemon thread group. A 
  147.      * daemon thread group is automatically destroyed when its last 
  148.      * thread is stopped or its last thread group is destroyed. 
  149.      *
  150.      * @return  <code>true</code> if this thread group is a daemon thread group;
  151.      *          <code>false</code> otherwise.
  152.      * @since   JDK1.0
  153.      */
  154.     public final boolean isDaemon() {
  155.     return daemon;
  156.     }
  157.  
  158.     /**
  159.      * Tests if this thread group has been destroyed.
  160.      *
  161.      * @since   JDK1.1
  162.      */
  163.     public synchronized boolean isDestroyed() {
  164.     return destroyed;
  165.     }
  166.  
  167.     /**
  168.      * Changes the daemon status of this thread group.
  169.      * <p>
  170.      * First, the <code>checkAccess</code> method of this thread group is 
  171.      * called with no arguments; this may result in a security exception. 
  172.      * <p>
  173.      * A daemon thread group is automatically destroyed when its last 
  174.      * thread is stopped or its last thread group is destroyed. 
  175.      *
  176.      * @param      daemon   if <code>true</code>, marks this thread group as
  177.      *                      a daemon thread group; otherwise, marks this
  178.      *                      thread group as normal.
  179.      * @exception  SecurityException  if the current thread cannot modify
  180.      *               this thread.
  181.      * @see        java.lang.SecurityException
  182.      * @see        java.lang.ThreadGroup#checkAccess()
  183.      * @since      JDK1.0
  184.      */
  185.     public final void setDaemon(boolean daemon) {
  186.     checkAccess();
  187.     this.daemon = daemon;
  188.     }
  189.  
  190.     /**
  191.      * Sets the maximum priority of the group. 
  192.      * <p>
  193.      * First, the <code>checkAccess</code> method of this thread group is 
  194.      * called with no arguments; this may result in a security exception. 
  195.      * <p>
  196.      * Threads in the thread group that already have a higher priority 
  197.      * are not affected. 
  198.      *
  199.      * @param      pri   the new priority of the thread group.
  200.      * @exception  SecurityException  if the current thread cannot modify
  201.      *               this thread group.
  202.      * @see        java.lang.SecurityException
  203.      * @see        java.lang.ThreadGroup#checkAccess()
  204.      * @since      JDK1.0
  205.      */
  206.     public final void setMaxPriority(int pri) {
  207.     int ngroupsSnapshot;
  208.     ThreadGroup[] groupsSnapshot;
  209.     synchronized (this) {
  210.         checkAccess();
  211.         if (pri < Thread.MIN_PRIORITY) {
  212.         maxPriority = Thread.MIN_PRIORITY;
  213.         } else if (pri < maxPriority) {
  214.         maxPriority = pri;
  215.         }
  216.         ngroupsSnapshot = ngroups;
  217.         if (groups != null) {
  218.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  219.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  220.         } else {
  221.         groupsSnapshot = null;
  222.         }
  223.     }
  224.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  225.         groupsSnapshot[i].setMaxPriority(pri);
  226.     }
  227.     }
  228.  
  229.     /**
  230.      * Tests if this thread group is either the thread group 
  231.      * argument or one of its ancestor thread groups. 
  232.      *
  233.      * @param   g   a thread group.
  234.      * @return  <code>true</code> if this thread group is the thread group
  235.      *          argument or one of its ancestor thread groups;
  236.      *          <code>false</code> otherwise.
  237.      * @since   JDK1.0
  238.      */
  239.     public final boolean parentOf(ThreadGroup g) {
  240.     for (; g != null ; g = g.parent) {
  241.         if (g == this) {
  242.         return true;
  243.         }
  244.     }
  245.     return false;
  246.     }
  247.  
  248.     /**
  249.      * Determines if the currently running thread has permission to 
  250.      * modify this thread group. 
  251.      * <p>
  252.      * If there is a security manager, its <code>checkAccess</code> method 
  253.      * is called with this thread group as its argument. This may result 
  254.      * in throwing a <code>SecurityException</code>. 
  255.      *
  256.      * @exception  SecurityException  if the current thread is not allowed to
  257.      *               access this thread group.
  258.      * @see        java.lang.SecurityManager#checkAccess(java.lang.ThreadGroup)
  259.      * @since      JDK1.0
  260.      */
  261.     public final void checkAccess() {
  262.     SecurityManager security = System.getSecurityManager();
  263.     if (security != null) {
  264.         security.checkAccess(this);
  265.     }
  266.     }
  267.  
  268.     /**
  269.      * Returns an estimate of the number of active threads in this
  270.      * thread group.
  271.      *
  272.      * @return  the number of active threads in this thread group and in any
  273.      *          other thread group that has this thread group as an ancestor.
  274.      * @since   JDK1.0
  275.      */
  276.     public int activeCount() {
  277.     int result;
  278.     // Snapshot sub-group data so we don't hold this lock
  279.     // while our children are computing.
  280.     int ngroupsSnapshot;
  281.     ThreadGroup[] groupsSnapshot;
  282.     synchronized (this) {
  283.         if (destroyed) {
  284.         return 0;
  285.         }
  286.         result = nthreads;
  287.         ngroupsSnapshot = ngroups;
  288.         if (groups != null) {
  289.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  290.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  291.         } else {
  292.         groupsSnapshot = null;
  293.         }
  294.     }
  295.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  296.         result += groupsSnapshot[i].activeCount();
  297.     }
  298.     return result;
  299.     }
  300.  
  301.     /**
  302.      * Copies into the specified array every active thread in this 
  303.      * thread group and its subgroups. 
  304.      * <p>
  305.      * An application should use the <code>activeCount</code> method to 
  306.      * get an estimate of how big the array should be. If the array is 
  307.      * too short to hold all the threads, the extra threads are silently 
  308.      * ignored. 
  309.      *
  310.      * @param   list   an array into which to place the list of threads.
  311.      * @return  the number of threads put into the array.
  312.      * @see     java.lang.ThreadGroup#activeCount()
  313.      * @since   JDK1.0
  314.      */
  315.     public int enumerate(Thread list[]) {
  316.     return enumerate(list, 0, true);
  317.     }
  318.  
  319.     /**
  320.      * Copies into the specified array every active thread in this 
  321.      * thread group. If the <code>recurse</code> flag is 
  322.      * <code>true</code>, references to every active thread in this 
  323.      * thread's subgroups are also included. If the array is too short to 
  324.      * hold all the threads, the extra threads are silently ignored. 
  325.      * <p>
  326.      * An application should use the <code>activeCount</code> method to 
  327.      * get an estimate of how big the array should be. 
  328.      *
  329.      * @param   list      an array into which to place the list of threads.
  330.      * @param   recurse   a flag indicating whether also to include threads
  331.      *                    in thread groups that are subgroups of this
  332.      *                    thread group.
  333.      * @return  the number of threads placed into the array.
  334.      * @see     java.lang.ThreadGroup#activeCount()
  335.      * @since   JDK1.0
  336.      */
  337.     public int enumerate(Thread list[], boolean recurse) {
  338.     return enumerate(list, 0, recurse);
  339.     }
  340.  
  341.     private int enumerate(Thread list[], int n, boolean recurse) {
  342.     int ngroupsSnapshot = 0;
  343.     ThreadGroup[] groupsSnapshot = null;
  344.     synchronized (this) {
  345.         if (destroyed) {
  346.         return 0;
  347.         }
  348.         int nt = nthreads;
  349.         if (nt > list.length - n) {
  350.         nt = list.length - n;
  351.         }
  352.         if (nt > 0) {
  353.         System.arraycopy(threads, 0, list, n, nt);
  354.         n += nt;
  355.         }
  356.         if (recurse) {
  357.         ngroupsSnapshot = ngroups;
  358.         if (groups != null) {
  359.             groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  360.             System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  361.         } else {
  362.             groupsSnapshot = null;
  363.         }
  364.         }
  365.     }
  366.     if (recurse) {
  367.         for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  368.         n = groupsSnapshot[i].enumerate(list, n, true);
  369.         }
  370.     }
  371.     return n;
  372.     }
  373.  
  374.     /**
  375.      * Returns an estimate of the number of active groups in this
  376.      * thread group.
  377.      *
  378.      * @return  the number of active thread groups with this thread group as
  379.      *          an ancestor.
  380.      * @since   JDK1.0
  381.      */
  382.     public int activeGroupCount() {
  383.     int ngroupsSnapshot;
  384.     ThreadGroup[] groupsSnapshot;
  385.     synchronized (this) {
  386.         if (destroyed) {
  387.         return 0;
  388.         }
  389.         ngroupsSnapshot = ngroups;
  390.         if (groups != null) {
  391.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  392.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  393.         } else {
  394.         groupsSnapshot = null;
  395.         }
  396.     }
  397.     int n = ngroupsSnapshot;
  398.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  399.         n += groupsSnapshot[i].activeGroupCount();
  400.     }
  401.     return n;
  402.     }
  403.  
  404.     /**
  405.      * Copies into the specified array references to every active 
  406.      * subgroup in this thread group. 
  407.      * <p>
  408.      * An application should use the <code>activeGroupCount</code> 
  409.      * method to get an estimate of how big the array should be. If the 
  410.      * array is too short to hold all the thread groups, the extra thread 
  411.      * groups are silently ignored. 
  412.      *
  413.      * @param   list   an array into which to place the list of thread groups.
  414.      * @return  the number of thread groups put into the array.
  415.      * @see     java.lang.ThreadGroup#activeGroupCount()
  416.      * @since   JDK1.0
  417.      */
  418.     public int enumerate(ThreadGroup list[]) {
  419.     return enumerate(list, 0, true);
  420.     }
  421.  
  422.     /**
  423.      * Copies into the specified array references to every active 
  424.      * subgroup in this thread group. If the <code>recurse</code> flag is 
  425.      * <code>true</code>, references to all active subgroups of the 
  426.      * subgroups and so forth are also included. 
  427.      * <p>
  428.      * An application should use the <code>activeGroupCount</code> 
  429.      * method to get an estimate of how big the array should be. 
  430.      *
  431.      * @param   list      an array into which to place the list of threads.
  432.      * @param   recurse   a flag indicating whether to recursively enumerate
  433.      *                    all included thread groups.
  434.      * @return  the number of thread groups put into the array.
  435.      * @see     java.lang.ThreadGroup#activeGroupCount()
  436.      * @since   JDK1.0
  437.      */
  438.     public int enumerate(ThreadGroup list[], boolean recurse) {
  439.     return enumerate(list, 0, recurse);
  440.     }
  441.  
  442.     private int enumerate(ThreadGroup list[], int n, boolean recurse) {
  443.     int ngroupsSnapshot = 0;
  444.     ThreadGroup[] groupsSnapshot = null;
  445.     synchronized (this) {
  446.         if (destroyed) {
  447.         return 0;
  448.         }
  449.         int ng = ngroups;
  450.         if (ng > list.length - n) {
  451.         ng = list.length - n;
  452.         }
  453.         if (ng > 0) {
  454.         System.arraycopy(groups, 0, list, n, ng);
  455.         n += ng;
  456.         }
  457.         if (recurse) {
  458.         ngroupsSnapshot = ngroups;
  459.         if (groups != null) {
  460.             groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  461.             System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  462.         } else {
  463.             groupsSnapshot = null;
  464.         }
  465.         }
  466.     }
  467.     if (recurse) {
  468.         for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  469.         n = groupsSnapshot[i].enumerate(list, n, true);
  470.         }
  471.     }
  472.     return n;
  473.     }
  474.  
  475.     /**
  476.      * Stops all processes in this thread group. 
  477.      * <p>
  478.      * First, the <code>checkAccess</code> method of this thread group is 
  479.      * called with no arguments; this may result in a security exception. 
  480.      * <p>
  481.      * This method then calls the <code>stop</code> method on all the 
  482.      * threads in this thread group and in all of its subgroups. 
  483.      *
  484.      * @exception  SecurityException  if the current thread is not allowed
  485.      *               to access this thread group or any of the threads in
  486.      *               the thread group.
  487.      * @see        java.lang.SecurityException
  488.      * @see        java.lang.Thread#stop()
  489.      * @see        java.lang.ThreadGroup#checkAccess()
  490.      * @since      JDK1.0
  491.      */
  492.     public final void stop() {
  493.     int ngroupsSnapshot;
  494.     ThreadGroup[] groupsSnapshot;
  495.     synchronized (this) {
  496.         checkAccess();
  497.         for (int i = 0 ; i < nthreads ; i++) {
  498.         threads[i].stop();
  499.         }
  500.         ngroupsSnapshot = ngroups;
  501.         if (groups != null) {
  502.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  503.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  504.         } else {
  505.         groupsSnapshot = null;
  506.         }
  507.     }
  508.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  509.         groupsSnapshot[i].stop();
  510.     }
  511.     }
  512.  
  513.     /**
  514.      * Suspends all processes in this thread group. 
  515.      * <p>
  516.      * First, the <code>checkAccess</code> method of this thread group is 
  517.      * called with no arguments; this may result in a security exception. 
  518.      * <p>
  519.      * This method then calls the <code>suspend</code> method on all the 
  520.      * threads in this thread group and in all of its subgroups. 
  521.      *
  522.      * @exception  SecurityException  if the current thread is not allowed
  523.      *               to access this thread group or any of the threads in
  524.      *               the thread group.
  525.      * @see        java.lang.SecurityException
  526.      * @see        java.lang.Thread#suspend()
  527.      * @see        java.lang.ThreadGroup#checkAccess()
  528.      * @since      JDK1.0
  529.      */
  530.     public final void suspend() {
  531.     int ngroupsSnapshot;
  532.     ThreadGroup[] groupsSnapshot;
  533.     synchronized (this) {
  534.         checkAccess();
  535.         for (int i = 0 ; i < nthreads ; i++) {
  536.         threads[i].suspend();
  537.         }
  538.         ngroupsSnapshot = ngroups;
  539.         if (groups != null) {
  540.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  541.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  542.         } else {
  543.         groupsSnapshot = null;
  544.         }
  545.     }
  546.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  547.         groupsSnapshot[i].suspend();
  548.     }
  549.     }
  550.  
  551.     /**
  552.      * Resumes all processes in this thread group. 
  553.      * <p>
  554.      * First, the <code>checkAccess</code> method of this thread group is 
  555.      * called with no arguments; this may result in a security exception. 
  556.      * <p>
  557.      * This method then calls the <code>resume</code> method on all the 
  558.      * threads in this thread group and in all of its sub groups. 
  559.      *
  560.      * @exception  SecurityException  if the current thread is not allowed to
  561.      *               access this thread group or any of the threads in the
  562.      *               thread group.
  563.      * @see        java.lang.SecurityException
  564.      * @see        java.lang.Thread#resume()
  565.      * @see        java.lang.ThreadGroup#checkAccess()
  566.      * @since      JDK1.0
  567.      */
  568.     public final void resume() {
  569.     int ngroupsSnapshot;
  570.     ThreadGroup[] groupsSnapshot;
  571.     synchronized (this) {
  572.         checkAccess();
  573.         for (int i = 0 ; i < nthreads ; i++) {
  574.         threads[i].resume();
  575.         }
  576.         ngroupsSnapshot = ngroups;
  577.         if (groups != null) {
  578.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  579.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  580.         } else {
  581.         groupsSnapshot = null;
  582.         }
  583.     }
  584.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  585.         groupsSnapshot[i].resume();
  586.     }
  587.     }
  588.  
  589.     /**
  590.      * Destroys this thread group and all of its subgroups. This thread 
  591.      * group must be empty, indicating that all threads that had been in 
  592.      * this thread group have since stopped. 
  593.      *
  594.      * @exception  IllegalThreadStateException  if the thread group is not
  595.      *               empty or if the thread group has already been destroyed.
  596.      * @exception  SecurityException  if the current thread cannot modify this
  597.      *               thread group.
  598.      * @since      JDK1.0
  599.      */
  600.     public final void destroy() {
  601.     int ngroupsSnapshot;
  602.     ThreadGroup[] groupsSnapshot;
  603.     synchronized (this) {
  604.         checkAccess();
  605.         if (destroyed || (nthreads > 0)) {
  606.         throw new IllegalThreadStateException();
  607.         }
  608.         ngroupsSnapshot = ngroups;
  609.         if (groups != null) {
  610.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  611.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  612.         } else {
  613.         groupsSnapshot = null;
  614.         }
  615.         if (parent != null) {
  616.         destroyed = true;
  617.         ngroups = 0;
  618.         groups = null;
  619.         nthreads = 0;
  620.         threads = null;
  621.         }
  622.     }
  623.     for (int i = 0 ; i < ngroupsSnapshot ; i += 1) {
  624.         groupsSnapshot[i].destroy();
  625.     }
  626.     if (parent != null) {
  627.         parent.remove(this);
  628.     }
  629.     }
  630.  
  631.     /**
  632.      * Adds the specified Thread group to this group.
  633.      * @param g the specified Thread group to be added
  634.      * @exception IllegalThreadStateException If the Thread group has been destroyed.
  635.      */
  636.     private final void add(ThreadGroup g){
  637.     synchronized (this) {
  638.         if (destroyed) {
  639.         throw new IllegalThreadStateException();
  640.         }
  641.         if (groups == null) {
  642.         groups = new ThreadGroup[4];
  643.         } else if (ngroups == groups.length) {
  644.         ThreadGroup newgroups[] = new ThreadGroup[ngroups * 2];
  645.         System.arraycopy(groups, 0, newgroups, 0, ngroups);
  646.         groups = newgroups;
  647.         }
  648.         groups[ngroups] = g;
  649.  
  650.         // This is done last so it doesn't matter in case the
  651.         // thread is killed
  652.         ngroups++;
  653.     }
  654.     }
  655.  
  656.     /**
  657.      * Removes the specified Thread group from this group.
  658.      * @param g the Thread group to be removed
  659.      * @return if this Thread has already been destroyed.
  660.      */
  661.     private void remove(ThreadGroup g) {
  662.     synchronized (this) {
  663.         if (destroyed) {
  664.         return;
  665.         }
  666.         for (int i = 0 ; i < ngroups ; i++) {
  667.         if (groups[i] == g) {
  668.             ngroups -= 1;
  669.             System.arraycopy(groups, i + 1, groups, i, ngroups - i);
  670.             // Zap dangling reference to the dead group so that
  671.             // the garbage collector will collect it.
  672.             groups[ngroups] = null;
  673.             break;
  674.         }
  675.         }
  676.         if (nthreads == 0) {
  677.         notifyAll();
  678.         }
  679.         if (daemon && (nthreads == 0) && (ngroups == 0)) {
  680.         destroy();
  681.         }
  682.     }
  683.     }
  684.     
  685.     /**
  686.      * Adds the specified Thread to this group.
  687.      * @param t the Thread to be added
  688.      * @exception IllegalThreadStateException If the Thread group has been destroyed.
  689.      */
  690.     void add(Thread t) {
  691.     synchronized (this) {
  692.         if (destroyed) {
  693.         throw new IllegalThreadStateException();
  694.         }
  695.         if (threads == null) {
  696.         threads = new Thread[4];
  697.         } else if (nthreads == threads.length) {
  698.         Thread newthreads[] = new Thread[nthreads * 2];
  699.         System.arraycopy(threads, 0, newthreads, 0, nthreads);
  700.         threads = newthreads;
  701.         }
  702.         threads[nthreads] = t;
  703.  
  704.         // This is done last so it doesn't matter in case the
  705.         // thread is killed
  706.         nthreads++;
  707.     }
  708.     }
  709.  
  710.     /**
  711.      * Removes the specified Thread from this group.
  712.      * @param t the Thread to be removed
  713.      * @return if the Thread has already been destroyed.
  714.      */
  715.     void remove(Thread t) {
  716.     synchronized (this) {
  717.         if (destroyed) {
  718.         return;
  719.         }
  720.         for (int i = 0 ; i < nthreads ; i++) {
  721.         if (threads[i] == t) {
  722.             System.arraycopy(threads, i + 1, threads, i, --nthreads - i);
  723.             // Zap dangling reference to the dead thread so that
  724.             // the garbage collector will collect it.
  725.             threads[nthreads] = null;
  726.             break;
  727.         }
  728.         }
  729.         if (nthreads == 0) {
  730.         notifyAll();
  731.         }
  732.         if (daemon && (nthreads == 0) && (ngroups == 0)) {
  733.         destroy();
  734.         }
  735.     }
  736.     }
  737.  
  738.     /**
  739.      * Prints information about this thread group to the standard 
  740.      * output. This method is useful only for debugging. 
  741.      *
  742.      * @since   JDK1.0
  743.      */
  744.     public void list() {
  745.     list(System.out, 0);
  746.     }
  747.     void list(PrintStream out, int indent) {
  748.     int ngroupsSnapshot;
  749.     ThreadGroup[] groupsSnapshot;
  750.     synchronized (this) {
  751.         for (int j = 0 ; j < indent ; j++) {
  752.         out.print(" ");
  753.         }
  754.         out.println(this);
  755.         indent += 4;
  756.         for (int i = 0 ; i < nthreads ; i++) {
  757.         for (int j = 0 ; j < indent ; j++) {
  758.             out.print(" ");
  759.         }
  760.         out.println(threads[i]);
  761.         }
  762.         ngroupsSnapshot = ngroups;
  763.         if (groups != null) {
  764.         groupsSnapshot = new ThreadGroup[ngroupsSnapshot];
  765.         System.arraycopy(groups, 0, groupsSnapshot, 0, ngroupsSnapshot);
  766.         } else {
  767.         groupsSnapshot = null;
  768.         }
  769.     }
  770.     for (int i = 0 ; i < ngroupsSnapshot ; i++) {
  771.         groupsSnapshot[i].list(out, indent);
  772.     }
  773.     }
  774.  
  775.     /**
  776.      * Called by the Java Virtual Machine when a thread in this 
  777.      * thread group stops because of an uncaught exception. 
  778.      * <p>
  779.      * The <code>uncaughtException</code> method of 
  780.      * <code>ThreadGroup</code> does the following: 
  781.      * <ul>
  782.      * <li>If this thread group has a parent thread group, the
  783.      *     <code>uncaughtException</code> method of that parent is called
  784.      *     with the same two arguments. 
  785.      * <li>Otherwise, this method determines if the <code>Throwable</code>
  786.      *     argument is an instance of <code>ThreadDeath</code>. If so, nothing
  787.      *     special is done. Otherwise, the <code>Throwable</code>'s
  788.      *     <code>printStackTrace</code> method is called to print a stack
  789.      *     backtrace to the standard error stream.
  790.      * </ul>
  791.      * <p>
  792.      * Applications can override this method in subclasses of 
  793.      * <code>ThreadGroup</code> to provide alternative handling of 
  794.      * uncaught exceptions. 
  795.      *
  796.      * @param   t   the thread that is about to exit.
  797.      * @param   e   the uncaught exception.
  798.      * @see     java.lang.System#err
  799.      * @see     java.lang.ThreadDeath
  800.      * @see     java.lang.Throwable#printStackTrace(java.io.PrintStream)
  801.      * @since   JDK1.0
  802.      */
  803.     public void uncaughtException(Thread t, Throwable e) {
  804.     if (parent != null) {
  805.         parent.uncaughtException(t, e);
  806.     } else if (!(e instanceof ThreadDeath)) {
  807.         e.printStackTrace(System.err);
  808.     }
  809.     }
  810.  
  811.     /**
  812.      * Used by VM to control lowmem implicit suspension.
  813.      *
  814.      * @since   JDK1.1
  815.      */
  816.     public boolean allowThreadSuspension(boolean b) {
  817.     this.vmAllowSuspension = b;
  818.     if (!b) {
  819.         VM.unsuspendSomeThreads();
  820.     }
  821.     return true;
  822.     }
  823.  
  824.     /**
  825.      * Returns a string representation of this Thread group.
  826.      *
  827.      * @return  a string representation of this thread group.
  828.      * @since   JDK1.0
  829.      */
  830.     public String toString() {
  831.     return getClass().getName() + "[name=" + getName() + ",maxpri=" + maxPriority + "]";
  832.     }
  833. }
  834.